﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyAlbum.Utility;
using System.Xml.Linq;
using System.IO;
using System.Collections.Specialized;
using MyAlbum.Logging;
using System.Collections.ObjectModel;
using System.Web;

namespace MyAlbum.Services
{
    public class Configuration : IConfiguration
    {
        private List<Extension> _extensions;
        private static List<ConfigItem> _values = new List<ConfigItem>();
        public ReadOnlyCollection<Extension> Extensions
        {
            get
            {
                return this._extensions.AsReadOnly();
            }
        }

        public ReadOnlyCollection<ConfigItem> Items
        {
            get
            {
                return (_values).AsReadOnly();
            }
        }
        public string ConfigFilePath { get; set; }
        private string rootDirectory;
        public Configuration(string rootDirectory)
        {
            Assert.IsNotNull(rootDirectory);
            this.ConfigFilePath = Path.Combine(rootDirectory, "App_Data/SiteConfig.xml");
            if (!File.Exists(this.ConfigFilePath))
            {
                throw new InvalidOperationException("File " + this.ConfigFilePath + " was not found");
            }
            this.rootDirectory = rootDirectory;
            LoadConfigValues();
            LoadThemes();
            LoadLanguages();
            return;
            LoadExtensions();
        }

        private void LoadExtensions()
        {
            this._extensions = new List<Extension>();
            var files = Directory.GetFiles(Path.Combine(rootDirectory, "Views/Shared/Extensions"), ".xml", SearchOption.TopDirectoryOnly);
            foreach (var item in files)
            {
                try
                {
                    this._extensions.Add(Extension.Deserialize(item));
                }
                catch (Exception e1)
                {
                    WriteToFile("error loading extension (" + item + "). Message:" + e1.Message);
                }
            }
        }

        private void LoadLanguages()
        {
            var config = _values.FirstOrDefault(i => i.Name == ConfigValues.DefaultLanguage);
            if (config != null)
            {
                if (config.ValueList == null)
                {
                    config.ValueList = new List<string>();
                }
                var names = Directory.GetDirectories(Path.Combine(this.rootDirectory, "Lang"));
                var changed = false;
                foreach (var item in names)
                {
                    if (File.Exists(Path.Combine(Path.Combine(this.rootDirectory, item), "labels.xml")))
                    {
                        if (config.ValueList.FirstOrDefault(s => s.Equals(Path.GetFileName(item), StringComparison.InvariantCultureIgnoreCase)) == null)
                        {
                            changed = true;
                            config.ValueList.Add(Path.GetFileName(item));
                        }
                    }
                }
                if (changed)
                {
                    XElement e = Document.Descendants("Config").SingleOrDefault(c => c.Attribute("name") != null && string.Compare(c.Attribute("name").Value, config.Name, true) == 0);
                    if (e != null)
                    {
                        e.Element("ValueList").ReplaceWith(new XElement("ValueList", string.Join(",", config.ValueList.ToArray())));
                    }
                    this.Document.Save(this.ConfigFilePath);
                }
            }

        }

        private void LoadThemes()
        {
            var config = _values.FirstOrDefault(i => i.Name == ConfigValues.Theme);
            if (config != null)
            {
                if (config.ValueList == null)
                {
                    config.ValueList = new List<string>();
                }
                var names = Directory.GetDirectories(Path.Combine(this.rootDirectory, "Content/Themes"));
                var changed = false;
                foreach (var item in names)
                {
                    if (config.ValueList.FirstOrDefault(s => s.Equals(Path.GetFileName(item), StringComparison.InvariantCultureIgnoreCase)) == null)
                    {
                        changed = true;
                        config.ValueList.Add(Path.GetFileName(item));
                    }
                }
                if (changed)
                {
                    XElement e = Document.Descendants("Config").SingleOrDefault(c => c.Attribute("name") != null && string.Compare(c.Attribute("name").Value, config.Name, true) == 0);
                    if (e != null)
                    {
                        e.Element("ValueList").ReplaceWith(new XElement("ValueList", string.Join(",", config.ValueList.ToArray())));
                    }
                    this.Document.Save(this.ConfigFilePath);
                }
            }
        }
        private XDocument _doc;
        public XDocument Document
        {
            get
            {
                if (_doc == null)
                {
                    _doc = XDocument.Parse(File.ReadAllText(ConfigFilePath));
                }
                return _doc;
            }
        }
        private void LoadConfigValues()
        {
            XDocument doc = Document;
            foreach (var item in doc.Descendants("Config"))
            {
                if (item.Attribute("name") != null)
                {
                    string name = item.Attribute("name").Value.ToLower();
                    if (item.Element("Value") != null && item.Element("Value").Value != null)
                    {
                        var c = new ConfigItem() { Name = name, Value = item.Element("Value").Value.Trim() };
                        if (item.Element("Description") != null && !item.Element("Description").IsEmpty)
                        {
                            c.Description = item.Element("Description").Value;

                        }
                        if (item.Attribute("type") != null)
                        {
                            try
                            {
                                c.ValidationType = (ValidationType)Enum.Parse(typeof(ValidationType), item.Attribute("type").Value, true);
                                if (c.ValidationType == ValidationType.selection)
                                {
                                    c.ValueList = new List<string>();
                                    if (item.Element("ValueList") != null && !item.Element("ValueList").IsEmpty)
                                    {
                                        c.ValueList.AddRange(item.Element("ValueList").Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
                                    }
                                }
                            }
                            catch
                            {
                                c.ValidationType = ValidationType.text;
                            }
                        }
                        if (item.Attribute("required") != null)
                        {
                            c.Required = TryConvert<bool>(item.Attribute("required").Value, false);
                        }
                        _values.Add(c);

                    }
                }
            }
        }

        public List<string> GetAvailableLanguages()
        {
            List<string> l;
            var c = _values.FirstOrDefault(i => i.Name == ConfigValues.DefaultLanguage);
            if (c == null)
            {
                l = new List<string>();
                l.Add("English");
            }
            else
            {
                l = c.ValueList;
            }
            return l;
        }

        private ConfigItem GetConfigItem(string name)
        {
            return _values.FirstOrDefault(i => i.Name == name);
        }

        public T GetValueOrDefault<T>(string name, T defaultValue) where T : IConvertible
        {
            ConfigItem c;
            if ((c = _values.FirstOrDefault(i => i.Name == name)) != null)
            {
                return TryConvert<T>(c.Value, defaultValue);
            }
            else
            {
                return defaultValue;
            }
        }

        /// <summary>
        /// Load a config value from the data base, returning the default value if the 
        /// value was not found or an error occurred converting to the specified type
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <param name="defaultValue"></param>
        /// 
        /// <returns></returns>
        public T TryConvert<T>(string value, T defaultValue) where T : IConvertible
        {
            try
            {
                if (typeof(T).IsEnum)
                {
                    return (T)Enum.Parse(typeof(T), value);
                }
                else
                {
                    return (T)Convert.ChangeType(value, typeof(T));
                }
            }
            catch (Exception e1)
            {

                return defaultValue;
            }
        }

        /// <summary>
        /// Gets a value indicating whether to show the statistics box
        /// </summary>
        public bool ShowStatisticsBox
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowStatisticsBox, true);
            }
        }

        /// <summary>
        /// Gets a value indicating whether to send an email verification to new users in order to validate their email address
        /// </summary>
        public bool ValidateNewUsersEmail
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ValidateNewUsersEmail, false);
            }
        }

        /// <summary>
        /// Gets a value indicating whether new users registration is allowed
        /// </summary>
        public bool AllowAddUsers
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.AllowAddUsers, false);
            }
        }

        public string ThemePath
        {
            get
            {
                return GetValueOrDefault<string>(ConfigValues.Theme, "Default");
                //return "Content/Themes/" + GetValueOrDefault<string>(ConfigValues.Theme, "mvc") + "/";
            }
        }
        public PersistenceModes PersistenceMode
        {
            get
            {
                return GetValueOrDefault<PersistenceModes>(ConfigValues.PersistanceMode, PersistenceModes.Xml);
            }
        }

        public bool UseGravators
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.UseGravators, false);
            }

        }


        public string DefaultLanguage
        {
            get
            {
                return GetValueOrDefault<string>(ConfigValues.DefaultLanguage, "English");
            }
        }


        public string SiteTitle
        {
            get
            {
                return GetValueOrDefault<string>(ConfigValues.SiteTitle, "");
            }
        }

        public string MailFromAddress
        {
            get
            {
                return GetValueOrDefault<string>(ConfigValues.MailFromAddress, null);
            }
        }

        public string MailUserName
        {
            get
            {
                return GetValueOrDefault<string>(ConfigValues.MailUserName, null);
            }
        }

        public string MailPassword
        {
            get
            {
                return GetValueOrDefault<string>(ConfigValues.MailPassword, null);
            }
        }

        public string MailHost
        {
            get
            {
                return GetValueOrDefault<string>(ConfigValues.MailHost, null);
            }
        }

        public bool ShowBlog
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowBlog, true);
            }
        }
        public bool UseCaptcha
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.UseCaptcha, true);
            }
        }
        public bool EmailOnReply
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.EmailOnReply, true);
            }
        }
        public bool EmailOnSupport
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.EmailOnSupport, true);
            }
        }

        public bool ShowTagsBox
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowTagsBox, true);
            }
        }



        public bool ShowRSS
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowRSS, true);
            }
        }


        public bool ShowTooltips
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowTooltips, true);
            }
        }


        public bool EmailOnErrors
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.EmailOnErrors, true);
            }
        }
        public bool UseCategories
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.UseCategories, true);
            }
        }

        public bool ShowEntriesByMonthsBox
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowEntriesByMonthsBox, true);
            }
        }

        public bool ShowSendToFriendBox
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowSendToFriendBox, true);
            }
        }

        public bool ShowLanguagesBox
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowLanguagesBox, true);
            }
        }

        public bool ShowValidHTMLMark
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowValidHTMLMark, true);
            }
        }

        public bool LogEntrances
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.LogEntrances, true);
            }
        }

        public bool ShowRandomImageBox
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowRandomImageBox, true);
            }
        }

        public bool ShowLatestEntryBox
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowLatestEntryBox, true);
            }
        }

        public bool ShowFlashTagsBox
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowFlashTagsBox, true);
            }
        }

        public bool FirstInstall
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.FirstInstall, true);
            }
        }

        public bool AutoApproveReplies
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.AutoApproveReplies, true);
            }
        }

        public bool ShowReplies
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowReplies, true);

            }
        }

        public bool EnableSlideShow
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.EnableSlideShow, true);
            }
        }
        public bool ShowAddRepliesBox
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.ShowAddRepliesBox, true);
            }
        }

        public int EntriesPerPage
        {
            get
            {
                return GetValueOrDefault<int>(ConfigValues.EntriesPerPage, 20);
            }
        }


        public int PostsPerPage
        {
            get
            {
                return GetValueOrDefault<int>(ConfigValues.PostsPerPage, 5);
            }
        }

        public int ThumbDefaultWidth
        {
            get
            {
                return GetValueOrDefault<int>(ConfigValues.ThumbDefaultWidth, 200);
            }
        }

        public int ThumbDefaultHeight
        {
            get
            {
                return GetValueOrDefault<int>(ConfigValues.ThumbDefaultHeight, 200);
            }
        }

        public int AlbumsPerRow
        {
            get
            {
                return GetValueOrDefault<int>(ConfigValues.AlbumsPerRow, 2);
            }
        }

        public int EntriesPerRow
        {
            get
            {
                return GetValueOrDefault<int>(ConfigValues.EntriesPerRow, 2);
            }
        }

        public int SectionsPerRow
        {
            get
            {
                return GetValueOrDefault<int>(ConfigValues.SectionsPerRow, 2);
            }
        }

        public string ConnectionString
        {
            get
            {
                return GetValueOrDefault<string>(ConfigValues.ConnectionString, null);
            }
        }

        public string AdminEmail
        {
            get
            {
                return GetValueOrDefault<string>(ConfigValues.AdminEmail, null);
            }
        }

        /// <summary>
        /// Gets a value indicating whether to add aspx file extension to the routes. Used for IIS6.
        /// </summary>
        public bool UseAspxExtensions
        {
            get
            {
                return GetValueOrDefault<bool>(ConfigValues.UseAspxExtensions, false);
            }
        }

        public void UpdateItems(NameValueCollection form)
        {
            bool changed = false;
            foreach (var config in _values)
            {
                if (form[config.Name] != null ||
                    (config.ValidationType == ValidationType.boolean &&
                    form[config.Name + "_description"] != null))
                {
                    string value = form[config.Name] ?? "false";
                    string description = form[config.Name + "_description"] ?? config.Description;
                    if (config.Value != value || config.Description != description)
                    {
                        /*TODO add validation*/
                        //for now validation is not required, only the site admin can
                        //change values and we the defaults anyway
                        switch (config.ValidationType)
                        {
                            case ValidationType.boolean:
                                if (value != "true" && value != "false")
                                {
                                    WriteToFile("invalid config value for " + config.Name + ":" + value);
                                    continue;
                                }
                                break;
                            case ValidationType.integer:
                                int j;
                                if (!int.TryParse(value, out j))
                                {
                                    WriteToFile("invalid config value for " +
                                        config.Name + ":" + value);
                                    continue;
                                }
                                break;
                            case ValidationType.numeric:
                                double k;
                                if (!double.TryParse(value,
                                    System.Globalization.NumberStyles.Any, null, out k))
                                {
                                    WriteToFile("invalid config value for " +
                                        config.Name + ":" + value);
                                    continue;
                                }
                                break;
                            case ValidationType.selection:
                                if (config.ValueList.SingleOrDefault(s => s.Equals(value, StringComparison.InvariantCultureIgnoreCase)) == null)
                                {
                                    WriteToFile("invalid config value for " +
                                                                            config.Name + ":" + value);
                                    continue;
                                }
                                break;
                            case ValidationType.text:

                                break;
                        }

                        config.Value = value;
                        changed = true;
                        XElement e = Document.Descendants("Config").SingleOrDefault(c => c.Attribute("name") != null && string.Compare(c.Attribute("name").Value, config.Name, true) == 0);
                        if (e != null)
                        {
                            e.Element("Value").ReplaceWith(new XElement("Value", value));
                            e.Element("Description").ReplaceWith(new XElement("Description", description));
                        }
                    }
                }

            }
            if (changed)
            {
                Document.Save(ConfigFilePath);
            }
        }

        private void WriteToFile(string message)
        {
            try
            {
                string path;
                if (HttpContext.Current != null)
                {
                    path = HttpContext.Current.Server.MapPath("~/App_Data");
                }
                else
                {
                    path = Directory.GetCurrentDirectory();
                }
                path = Path.Combine(path, "errors" + DateTime.Now.ToString("yyyy_MM_dd") + ".txt");
                File.AppendAllText(path, message);
            }
            catch { }
        }
        public class ConfigValues
        {
            public const string ShowStatisticsBox = "show statistics box";
            public const string ValidateNewUsersEmail = "validate new users email address";
            public const string AllowAddUsers = "allow new users registration";
            public const string UseAspxExtensions = "use aspx extension for routes";
            public const string Theme = "theme";
            public const string ShowBlog = "show blog";
            public const string PostsPerPage = "posts per page";
            public const string UseCaptcha = "use captcha for replies and send to friend";
            public const string EnableSlideShow = "enable slide show per album";
            public const string EmailOnReply = "email admin on new replies";
            public const string EmailOnSupport = "email admin on support request";
            public const string EmailOnErrors = "email admin on errors";
            public const string ShowTooltips = "show tooltip";
            public const string SiteTitle = "site title";
            public const string PersistanceMode = "persistence mode";
            public const string DefaultLanguage = "default language";
            public const string MailFromAddress = "mail from address";
            public const string MailUserName = "mail user name";
            public const string MailPassword = "mail password";
            public const string MailHost = "mail host";
            public const string ShowTagsBox = "show tags cloud box";
            public const string UseCategories = "use categories";
            public const string ShowEntriesByMonthsBox = "show entries count by months box";
            public const string ShowSendToFriendBox = "show send to friend box";
            public const string ShowLanguagesBox = "show languages box";
            public const string ShowValidHTMLMark = "show valid html mark";
            public const string LogEntrances = "log entrances";
            public const string ShowRandomImageBox = "show random image box";
            public const string ShowLatestEntryBox = "show latest entry box";
            public const string ShowFlashTagsBox = "show flash tags box";
            public const string AutoApproveReplies = "auto approve replies";
            public const string ShowReplies = "show replies";
            public const string ShowAddRepliesBox = "show add reply box";
            public const string EntriesPerPage = "entries per page";
            public const string ThumbDefaultWidth = "thumb default width";
            public const string ThumbDefaultHeight = "thumb default height";
            public const string AlbumsPerRow = "albums per row";
            public const string EntriesPerRow = "entries per row";
            public const string SectionsPerRow = "sections per row";
            public const string AdminEmail = "admin email";
            public const string ConnectionString = "connnection string";
            public const string FirstInstall = "first install";
            public const string ShowRSS = "show rss";
            public const string EnableTrace = "enable trace";
            public const string UseGravators="use gravators";

        }



        public bool EnableTrace
        {
            get { return GetValueOrDefault<bool>(ConfigValues.EnableTrace, true); }
        }


    }
}
